package core

import (
	"crypto/aes"
	"crypto/cipher"
	"encoding/base64"
	"encoding/json"
	"github.com/astaxie/beego/logs"
	"io/ioutil"
	"net/http"
	"strings"
	"time"
)

//这里主要提供微信小程序和公众号的一些统一接口
//并维护一个分游戏的access_token

var wxTokenMap map[string]WxAccessToken

var wxConfMap map[string]WxConf

func init() {
	wxTokenMap = make(map[string]WxAccessToken)
	wxConfMap = make(map[string]WxConf)
	//初始化配置
	wxConfMap["popgame"] = WxConf{
		AppId:    "wx7b646d0ae6fc3e39",
		Secret:   "265558a59d5c66e9302b9de710c74aa0",
		OfferId:  "1450014805",
		MidasKey: "NKLErYGYK0VLdMKX84OTvdbh3DYkdCvz",
		PF:       "android",
	}
	wxConfMap["tower"] = WxConf{
		AppId:    "wxb9a0c3d6005fd2b2",
		Secret:   "2f67e082f28d362ab91dcbdcc5261c1c",
		OfferId:  "1450015273",
		MidasKey: "14Hv9VFtqs7vvKvHslQlNIFaVVN2ze0F",
		PF:       "android",
	}
	autoFreshAccessToken("popgame")
	autoFreshAccessToken("tower")
}

func autoFreshAccessToken(gmaeName string) {
	var afterFunc func()
	afterFunc = func() {
		FreshAccessToken(gmaeName)
		time.AfterFunc(time.Second*7100, afterFunc)
	}
	FreshAccessToken(gmaeName)
	time.AfterFunc(time.Second*7100, afterFunc)
}

type WxConf struct {
	AppId    string `json:"appid"`
	Secret   string `json:"secret"`
	OfferId  string `json:"offer_id"`
	MidasKey string `json:"midas_key"`
	PF       string `json:"pf"`
}

type WxAccessToken struct {
	//微信接口的调用凭证
	AccessToken string `json:"access_token"`
	//过期时间
	ExpiresIn int32 `json:"expires_in"`
	//错误码
	errcode int32 `json:"errcode"`
	//错误信息
	errmsg string `json:"errmsg"`
}

func GetWXConf(gameName string) WxConf {
	return wxConfMap[gameName]
}

//获取微信公众平台AccessToken
func GetAccessToken(gameName string) string {
	return wxTokenMap[gameName].AccessToken
}

//刷新accessToken
func FreshAccessToken(gameName string) {
	conf := wxConfMap[gameName]

	url := strings.Builder{}
	url.WriteString("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=")
	url.WriteString(conf.AppId)
	url.WriteString("&secret=")
	url.WriteString(conf.Secret)

	resp, err := http.Get(url.String())

	if err != nil {
		logs.Error("FreshAccessToken http get ", err)
		return
	}

	body, _ := ioutil.ReadAll(resp.Body)
	logs.Info("resp [%s]", string(body))
	accessToken := WxAccessToken{}
	err = json.Unmarshal(body, &accessToken)
	if err != nil {
		logs.Error("FreshAccessToken json Unmarshal ", err)
		return
	}

	logs.Info(" %s accessToken [%+v]", gameName, accessToken)
	//存起来
	wxTokenMap[gameName] = accessToken
}

type WxDataSt struct {
	OpenID    string `json:"openId"`
	NickName  string `json:"nickName"`
	Gender    int    `json:"gender"`
	Language  string `json:"language"`
	City      string `json:"city"`
	Province  string `json:"province"`
	Country   string `json:"country"`
	AvatarURL string `json:"avatarUrl"`
	UnionID   string `json:"unionId"`
	Watermark struct {
		Timestamp int    `json:"timestamp"`
		Appid     string `json:"appid"`
	} `json:"watermark"`
}

//解析微信加密数据，取出unionId
func AnalyzeWxData(encryptedData string, ivData string, sessionKey string) (*WxDataSt, error) {
	ciphertext, err := base64.StdEncoding.DecodeString(encryptedData)
	if err != nil {
		logs.Error(err)
		return nil, err
	}
	//logs.Info("ciphertext %s",string(ciphertext))

	iv, err := base64.StdEncoding.DecodeString(ivData)
	if err != nil {
		logs.Error(err)
		return nil, err
	}
	logs.Info("iv %s",string(iv))

	key, err := base64.StdEncoding.DecodeString(sessionKey)
	if err != nil {
		logs.Error(err)
		return nil, err
	}

	logs.Info("key %s",string(key))

	result, err := Decrypt(ciphertext, key, iv)
	if err != nil {
		logs.Error(err)
		return nil, err
	}

	logs.Info(string(result))

	wxData := WxDataSt{}
	err = json.Unmarshal(result, &wxData)
	if err != nil {
		return nil, err
	}

	return &wxData, nil
}

func Decrypt(ciphertext, key []byte, vi []byte) ([]byte, error) {
	keyBytes := []byte(key)
	block, err := aes.NewCipher(keyBytes) //选择加密算法
	if err != nil {
		return nil, err
	}
	blockModel := cipher.NewCBCDecrypter(block, vi)
	plantText := make([]byte, len(ciphertext))
	blockModel.CryptBlocks(plantText, ciphertext)
	plantText = PKCS7UnPadding(plantText, block.BlockSize())
	return plantText, nil
}

func PKCS7UnPadding(plantText []byte, blockSize int) []byte {
	length := len(plantText)
	unpadding := int(plantText[length-1])
	return plantText[:(length - unpadding)]
}
